<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>敏感配置加密工具</title>
    <link rel="stylesheet" href="/css/bootstrap.min.css">
    <style>
        body {
            padding-top: 4.5rem;
            padding-bottom: 2rem;
            background-color: #f8f9fa;
            min-height: 100vh;
        }
        .container {
            max-width: 800px;
            margin-top: 1rem;
        }
        .form-group {
            margin-bottom: 1.5rem;
        }
        textarea {
            font-family: monospace;
            resize: vertical;
            min-height: 200px;
        }
        .result-container {
            margin-top: 2rem;
            padding: 1rem;
            background-color: #fff;
            border-radius: 0.25rem;
            border: 1px solid #dee2e6;
        }
        .error-message {
            color: #dc3545;
            font-size: 0.875rem;
            margin-top: 0.25rem;
        }
        .line-error {
            background-color: rgba(220, 53, 69, 0.1);
        }
        .line-highlight {
            background-color: #ffff99;
        }
        .validation-feedback {
            margin-top: 0.5rem;
            padding: 0.5rem;
            border-radius: 0.25rem;
            font-size: 0.875rem;
        }
        .validation-success {
            color: #198754;
            background-color: rgba(25, 135, 84, 0.1);
            border: 1px solid rgba(25, 135, 84, 0.2);
        }
        .validation-error {
            color: #dc3545;
            background-color: rgba(220, 53, 69, 0.1);
            border: 1px solid rgba(220, 53, 69, 0.2);
        }
        .validation-warning {
            color: #ffc107;
            background-color: rgba(255, 193, 7, 0.1);
            border: 1px solid rgba(255, 193, 7, 0.2);
        }
        .nav-tabs .nav-link {
            cursor: pointer;
        }
        .result-tab-content {
            max-height: 400px;
            overflow-y: auto;
        }
    </style>
</head>
<body>
    <!-- 导航栏 -->
    <nav class="navbar navbar-expand-lg navbar-dark bg-dark fixed-top">
        <div class="container">
            <a class="navbar-brand" href="/">敏感配置加密生成器</a>
            <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" 
                    aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
                <span class="navbar-toggler-icon"></span>
            </button>
            <div class="collapse navbar-collapse" id="navbarNav">
                <ul class="navbar-nav">
                    <li class="nav-item">
                        <a class="nav-link active" href="/">Properties加密</a>
                    </li>
                    <li class="nav-item">
                        <a class="nav-link" href="/yaml">YAML加密</a>
                    </li>
                    <!--<li class="nav-item">
                        <a class="nav-link" href="/keygen">密钥生成器</a>
                    </li>-->
                </ul>
            </div>
        </div>
    </nav>
    
    <div class="container">
        <h1 class="text-center mb-4">Properties 敏感配置加密工具</h1>
        
        <div class="card">
            <div class="card-body">
                <form th:action="@{/process}" th:object="${propertiesRequest}" method="post" id="propertiesForm">
                    <div class="form-group">
                        <label for="projectName" class="form-label">项目名称：</label>
                        <input type="text" id="projectName" th:field="*{projectName}" 
                               class="form-control" placeholder="请输入项目名称(只能-拼接)，示例：global-website-b2c"
                               th:classappend="${#fields.hasErrors('projectName')} ? 'is-invalid' : ''">
                        <div class="error-message" th:if="${#fields.hasErrors('projectName')}" 
                             th:errors="*{projectName}"></div>
                    </div>
                    
                    <div class="form-group">
                        <label for="propertiesContent" class="form-label">请输入Properties内容（格式：key=value）：</label>
                        <div class="mb-2">
                            <button type="button" class="btn btn-sm btn-outline-secondary" onclick="fillExample()">填充示例</button>
                            <button type="button" class="btn btn-sm btn-outline-danger ms-2" onclick="clearContent()">清空内容</button>
                        </div>
                        <textarea id="propertiesContent" th:field="*{propertiesContent}" 
                                  class="form-control" rows="10" 
                                  th:classappend="${#fields.hasErrors('propertiesContent')} ? 'is-invalid' : ''"
                                  onkeyup="validateProperties(this.value)"></textarea>
                        <div class="error-message" th:if="${#fields.hasErrors('propertiesContent')}" 
                             th:errors="*{propertiesContent}"></div>
                        <div class="form-text">每行一个键值对，格式为key=value</div>
                        <div id="validationFeedback" class="validation-feedback d-none"></div>
                    </div>
                    
                    <div class="d-grid">
                        <button type="submit" class="btn btn-primary" id="submitBtn">加密处理</button>
                    </div>
                </form>
            </div>
        </div>
        
        <div class="result-container" th:if="${hasResults}">
            <h4>敏感配置加密结果：</h4>
            
            <ul class="nav nav-tabs mb-3" id="resultTabs" role="tablist">
                <li class="nav-item" role="presentation">
                    <button class="nav-link active" id="md5-tab" data-bs-toggle="tab" data-bs-target="#md5" type="button" role="tab" aria-controls="md5" aria-selected="true">生成的本地敏感配置</button>
                </li>
                <li class="nav-item" role="presentation">
                    <button class="nav-link" id="base64-tab" data-bs-toggle="tab" data-bs-target="#base64" type="button" role="tab" aria-controls="base64" aria-selected="false">keyvault配置</button>
                </li>
                <li class="nav-item" role="presentation">
                    <button class="nav-link" id="keyvault-tab" data-bs-toggle="tab" data-bs-target="#keyvault" type="button" role="tab" aria-controls="keyvault" aria-selected="false">KeyVault执行命令</button>
                </li>
            </ul>
            
            <div class="tab-content mt-2 result-tab-content">
                <div class="tab-pane fade show active" id="md5" role="tabpanel" aria-labelledby="md5-tab">
                    <pre th:utext="${md5Result}" class="bg-light p-3 rounded"></pre>
                    <div class="d-grid mt-2">
                        <button class="btn btn-outline-secondary btn-sm" onclick="copyResult('md5')">复制本地敏感配置结果</button>
                    </div>
                </div>
                <div class="tab-pane fade" id="base64" role="tabpanel" aria-labelledby="base64-tab">
                    <pre th:utext="${base64Result}" class="bg-light p-3 rounded"></pre>
                    <div class="d-grid mt-2">
                        <button class="btn btn-outline-secondary btn-sm" onclick="copyResult('base64')">复制keyvault配置结果</button>
                    </div>
                </div>
                <div class="tab-pane fade" id="keyvault" role="tabpanel" aria-labelledby="keyvault-tab">
                    <pre th:utext="${keyvaultCommands}" class="bg-light p-3 rounded"></pre>
                    <div class="d-grid mt-2">
                        <button class="btn btn-outline-secondary btn-sm" onclick="copyResult('keyvault')">复制KeyVault执行命令</button>
                    </div>
                </div>
            </div>
            
            <div class="alert alert-info mt-3">
                <strong>提示：</strong> 点击上方标签页可以切换查看不同的加密结果
            </div>
        </div>
    </div>
    
    <script src="/js/bootstrap.bundle.min.js"></script>
    <script>
        // 复制结果到剪贴板
        function copyResult(type) {
            const resultElement = document.querySelector(`#${type} pre`);
            const resultText = resultElement ? resultElement.textContent : '';
            
            navigator.clipboard.writeText(resultText)
                .then(() => {
                    alert('已复制到剪贴板！');
                })
                .catch(err => {
                    console.error('复制失败: ', err);
                    alert('复制失败，请手动复制。');
                });
        }
        
        // Properties格式校验
        function validateProperties(text) {
            const feedbackElement = document.getElementById('validationFeedback');
            const submitBtn = document.getElementById('submitBtn');
            
            // 如果输入为空，隐藏反馈信息
            if (!text.trim()) {
                feedbackElement.classList.add('d-none');
                submitBtn.disabled = true;
                return;
            }
            
            // 分析每一行
            const lines = text.split(/\r?\n/);
            let isValid = true;
            let errorMessages = [];
            let lineErrors = [];
            
            // 检查每一行是否符合key=value格式
            for (let i = 0; i < lines.length; i++) {
                const line = lines[i].trim();
                
                // 跳过空行
                if (!line) continue;
                
                // 检查是否包含等号
                const equalIndex = line.indexOf('=');
                if (equalIndex === -1) {
                    lineErrors.push(i + 1);
                    errorMessages.push(`第 ${i + 1} 行缺少等号(=)`);
                    isValid = false;
                    continue;
                }
                
                // 检查key是否有效（只允许字母、数字、点、下划线和连字符）
                // 重要：只取第一个等号前面的部分作为key，允许value中包含任意数量的等号
                const key = line.substring(0, equalIndex).trim();
                
                if (!key) {
                    lineErrors.push(i + 1);
                    errorMessages.push(`第 ${i + 1} 行缺少键名`);
                    isValid = false;
                    continue;
                }
                
                if (!/^[\w.\-]+$/.test(key)) {
                    lineErrors.push(i + 1);
                    errorMessages.push(`第 ${i + 1} 行键名 "${key}" 包含无效字符（只允许字母、数字、点、下划线和连字符）`);
                    isValid = false;
                }
                
                // 注意：我们不验证value部分，value可以包含任何字符，包括多个等号
            }
            
            // 显示验证结果
            feedbackElement.classList.remove('d-none', 'validation-success', 'validation-error', 'validation-warning');
            
            if (isValid) {
                feedbackElement.classList.add('validation-success');
                feedbackElement.innerHTML = '✓ Properties格式正确';
                submitBtn.disabled = false;
            } else {
                feedbackElement.classList.add('validation-error');
                feedbackElement.innerHTML = '✗ Properties格式错误:<br>' + errorMessages.join('<br>');
                submitBtn.disabled = true;
                
                // 高亮显示错误行
                highlightErrorLines(lineErrors);
            }
        }
        
        // 高亮显示错误行
        function highlightErrorLines(lineNumbers) {
            const textarea = document.getElementById('propertiesContent');
            
            // 清除之前的高亮样式
            textarea.classList.remove('line-error');
            
            if (lineNumbers.length === 0) return;
            
            // 添加错误样式类到文本框
            textarea.classList.add('line-error');
            
            // 记住当前选中位置和滚动位置
            const currentPosition = textarea.selectionStart;
            const currentScroll = textarea.scrollTop;
            
            // 聚焦到第一个错误行，但保持用户的编辑点
            if (lineNumbers.length > 0) {
                try {
                    // 获取行高以便滚动到正确位置
                    const lineHeight = parseInt(window.getComputedStyle(textarea).lineHeight);
                    const firstErrorLine = lineNumbers[0] - 1;
                    const scrollTarget = lineHeight * firstErrorLine;
                    
                    // 先聚焦
                    textarea.focus();
                    
                    // 恢复之前的光标位置
                    textarea.setSelectionRange(currentPosition, currentPosition);
                    
                    // 只有当错误行不在可视区域时才滚动
                    if (scrollTarget < currentScroll || 
                        scrollTarget > (currentScroll + textarea.clientHeight - lineHeight)) {
                        textarea.scrollTop = scrollTarget;
                    } else {
                        // 否则保持当前滚动位置
                        textarea.scrollTop = currentScroll;
                    }
                } catch (e) {
                    console.error('设置光标或滚动位置失败:', e);
                }
            }
        }
        
        // 页面加载时初始化验证和标签页
        document.addEventListener('DOMContentLoaded', function() {
            const textarea = document.getElementById('propertiesContent');
            if (textarea.value) {
                validateProperties(textarea.value);
            } else {
                document.getElementById('submitBtn').disabled = true;
            }
            
            // 表单提交前再次验证
            document.getElementById('propertiesForm').addEventListener('submit', function(event) {
                const text = textarea.value.trim();
                if (!text) {
                    event.preventDefault();
                    alert('请输入Properties内容');
                    return false;
                }
                
                // 验证格式
                const lines = text.split(/\r?\n/);
                let isValid = true;
                
                for (let i = 0; i < lines.length; i++) {
                    const line = lines[i].trim();
                    if (!line) continue;
                    
                    // 只检查key部分是否有效，value可以包含任何字符（包括等号）
                    const equalIndex = line.indexOf('=');
                    // 如果没有等号或等号前面没有内容，则格式无效
                    if (equalIndex === -1 || line.substring(0, equalIndex).trim() === '') {
                        isValid = false;
                        break;
                    }
                    
                    // 确保key只包含字母、数字、点、下划线和连字符
                    const key = line.substring(0, equalIndex).trim();
                    if (!/^[\w.\-]+$/.test(key)) {
                        isValid = false;
                        break;
                    }
                }
                
                if (!isValid) {
                    event.preventDefault();
                    alert('Properties格式不正确，请修正后再提交');
                    return false;
                }
            });
            
            // 初始化标签页
            if (document.getElementById('resultTabs')) {
                // 使用Bootstrap的Tab组件
                const triggerTabList = [].slice.call(document.querySelectorAll('#resultTabs button'));
                triggerTabList.forEach(function(triggerEl) {
                    const tabTrigger = new bootstrap.Tab(triggerEl);
                    
                    triggerEl.addEventListener('click', function(event) {
                        event.preventDefault();
                        tabTrigger.show();
                    });
                });
            }
        });
        
        // 填充示例内容
        function fillExample() {
            const textarea = document.getElementById('propertiesContent');
            textarea.value = `db.username=admin
db.password=123456
app.secret=mysecretkey
api.url=https://api.example.com
app.version=1.0.0
jdbc.url=jdbc:mysql://localhost:3306/mydb?characterEncoding=UTF-8&useSSL=false
spring.datasource.url=jdbc:postgresql://postgres:5432/mydb?currentSchema=public
spring.activemq.broker-url=tcp://localhost:61616?jms.useAsyncSend=true&jms.prefetchPolicy.queuePrefetch=1
app.equation=E=mc²
app.query=SELECT * FROM users WHERE name='test' AND password='test123'
my-custom-property=value-with-dashes
spring-boot.application-name=my-cool-app`;
            validateProperties(textarea.value);
        }
        
        // 清空内容
        function clearContent() {
            const textarea = document.getElementById('propertiesContent');
            textarea.value = '';
            validateProperties('');
            document.getElementById('validationFeedback').classList.add('d-none');
        }
    </script>
</body>
</html> 